package com.lmxdawn.api.dnf.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.lmxdawn.api.admin.entity.auth.AuthAdmin;
import com.lmxdawn.api.admin.entity.auth.AuthPermission;
import com.lmxdawn.api.admin.entity.auth.AuthPermissionRule;
import com.lmxdawn.api.admin.entity.auth.AuthRoleAdmin;
import com.lmxdawn.api.admin.exception.JsonException;
import com.lmxdawn.api.admin.service.auth.AuthAdminService;
import com.lmxdawn.api.admin.service.auth.AuthPermissionRuleService;
import com.lmxdawn.api.admin.service.auth.AuthPermissionService;
import com.lmxdawn.api.admin.service.auth.AuthRoleAdminService;
import com.lmxdawn.api.admin.util.CacheUtils;
import com.lmxdawn.api.admin.util.PasswordUtils;
import com.lmxdawn.api.common.api.*;
import com.lmxdawn.api.common.autoconfigure.SessionProperties;
import com.lmxdawn.api.common.enums.ResultEnum;
import com.lmxdawn.api.common.session.SessionContextHolder;
import com.lmxdawn.api.common.session.SessionHelper;
import com.lmxdawn.api.common.session.model.LoginModel;
import com.lmxdawn.api.common.session.model.LoginUser;
import com.lmxdawn.api.common.util.IdWorker;
import com.lmxdawn.api.dnf.entity.RealName;
import com.lmxdawn.api.dnf.service.IRealnameService;
import com.lmxdawn.api.dnf.dto.SysSmsCodeDto;
import com.lmxdawn.api.dnf.dto.request.LoginReq;
import com.lmxdawn.api.dnf.dto.response.LoginResp;
import com.lmxdawn.api.dnf.service.ISmsService;
import com.lmxdawn.api.dnf.service.IWebLoginService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * web端登录服务实现类
 */
@Slf4j
@Service
public class WebLoginServiceImpl implements IWebLoginService {

    /**
     * 用户权限缓存KEY前缀
     */
    private static final String LOGIN_PERMISSION_KEY_PREFIX = "bp:login:permission:";
    /**
     * 用户权限过期时间，单位秒，默认60分钟
     */
    private static final Integer LOGIN_PERMISSION_EXPIRE_TIME = 3600;

    @Resource
    private SessionProperties properties;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private AuthAdminService authAdminService;

    @Resource
    private IRealnameService realnameService;

    @Resource
    private AuthRoleAdminService authRoleAdminService;

    @Resource
    private AuthPermissionService authPermissionService;

    @Resource
    private AuthPermissionRuleService authPermissionRuleService;

    @Resource
    private ISmsService smsService;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public LoginResp login(LoginReq loginReq) {
        String token = this.doLogin(loginReq);
        LoginResp loginResp = new LoginResp();
        loginResp.setToken(token);
        return loginResp;
    }

    @Override
    public void sendPhoneCode(LoginReq loginReq) {
//        AuthAdmin authAdmin = authAdminService.findByUserName(loginReq.getPhone());
//        ApiAssert.isNull(authAdmin, UserErrorCode.PHONE_UNREGISTERED);
//        ApiAssert.equals(authAdmin.getStatus(), 0, UserErrorCode.ACCOUNT_FORBIDDEN);
        // 发送短信验证码
        smsService.sendLoginVerifyCode(loginReq.getPhone());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginResp phoneCodeLogin(LoginReq loginReq) {
        RAtomicLong atomicLong = redissonClient.getAtomicLong("USER_ID");
        RLock userLock = redissonClient.getLock(String.format("USER_LOCK_%s", loginReq.getPhone()));
        if (!userLock.tryLock()){
            throw new RuntimeException("系统繁忙");
        }
        try {
            AuthAdmin authAdmin = authAdminService.findByUserName(loginReq.getPhone());
            if (Objects.nonNull(authAdmin)) {
                ApiAssert.equals(authAdmin.getStatus(), 0, UserErrorCode.ACCOUNT_FORBIDDEN);
            }
            if ("18006910491".equals(loginReq.getPhone()) && "888888".equals(loginReq.getPhoneCode())){

                if (Objects.isNull(authAdmin)) {
                    //当前手机号完成手机验证，自动进行注册
                    authAdmin = new AuthAdmin();
                    String adminId = "user-"+idWorker.nextId();
                    authAdmin.setId(adminId);
                    authAdmin.setUsername(loginReq.getPhone());
                    authAdmin.setPassword(PasswordUtils.authAdminPwd("ab123456"));
                    authAdmin.setStatus(Constants.ONE);
                    authAdminService.insertAuthAdmin(authAdmin);
                    authRoleAdminService.insertRolesAdminIdAll(Collections.singletonList(12L), adminId);
                }
                String token = this.doLogin(loginReq);
                LoginResp loginResp = new LoginResp();
                loginResp.setToken(token);
                return loginResp;
            }else{
                String redisKey = String.format(RedisKeyEnum.LOGIN_SMS_CODE.getKey(), loginReq.getPhone());
                Object str = CacheUtils.get(redisKey);
                ApiAssert.isNull(str, UserErrorCode.PHONECODE_ERROR);
                SysSmsCodeDto smsCodeDto = JSON.parseObject(str.toString(), SysSmsCodeDto.class);

                ApiAssert.nonEquals(smsCodeDto.getCode(), loginReq.getPhoneCode(), UserErrorCode.PHONECODE_ERROR);
                // 验证码间隔时间
                long diffSecond = (SystemClock.now() - smsCodeDto.getTimestamp()) / 1000;
                if (Constants.ONE.equals(smsCodeDto.getStatus()) || diffSecond > 300) {
                    ApiAssert.fail(UserErrorCode.PHONECODE_LOSE_EXPIRED);
                }
                if (Objects.isNull(authAdmin)) {
                    //当前手机号完成手机验证，自动进行注册
                    authAdmin = new AuthAdmin();
                    String adminId = "user-"+idWorker.nextId();
                    authAdmin.setId(adminId);
                    authAdmin.setUsername(loginReq.getPhone());
                    authAdmin.setPassword(PasswordUtils.authAdminPwd("ab123456"));
                    authAdmin.setStatus(Constants.ONE);
                    authAdmin.setCode(loginReq.getType() + "-"
                            +DateUtil.format(DateUtil.date(), "yyyyMMdd")
                            + "-" + StringUtils.leftPad(atomicLong.getAndIncrement()+"",6, "0"));
                    authAdminService.insertAuthAdmin(authAdmin);
                    authRoleAdminService.insertRolesAdminIdAll(Collections.singletonList(12L), adminId);
                }
                String token = this.doLogin(loginReq);
                LoginResp loginResp = new LoginResp();
                loginResp.setToken(token);


                smsCodeDto.setStatus(Constants.ONE);
                // 删除验证码缓存
                CacheUtils.delete(redisKey);

                return loginResp;
            }
        }catch (Exception exception){
            log.error(exception.getMessage());
            throw new RuntimeException(exception);
        }finally {
            userLock.unlock();
        }

    }

    private String doLogin(LoginReq loginReq) {
        AuthAdmin authAdmin = authAdminService.findByUserName(loginReq.getPhone());
        if (Objects.nonNull(loginReq.getPassword()))
        validatePassWord(loginReq, authAdmin);
        // 更新登录状态
        AuthAdmin authAdminUp = new AuthAdmin();
        authAdminUp.setId(authAdmin.getId());
        authAdminUp.setLastLoginTime(new Date());
        authAdminService.updateAuthAdmin(authAdminUp);
        // 登录成功后获取权限，将权限设置到缓存

        // 新增用户权限校验
        this.refreshPermissionCache(authAdmin.getId());
        List<String> permissionList = this.getPermissionFromCache(authAdmin.getId());
        ApiAssert.isEmpty(permissionList, ApiErrorCode.NOT_PERMISSION);
        RealName one = realnameService.lambdaQuery().eq(RealName::getUserId, authAdminUp.getId()).one();
        LoginModel loginModel = LoginModel.builder().device("pc")
                .timeout(properties.getWebTimeout()).build();
        LoginUser loginUser = new LoginUser();
        loginUser.setId(authAdmin.getId());
        loginUser.setRealName(one != null && one.getStatus() != 0);
        loginUser.setTipRead(authAdmin.getTipStatus() !=null && authAdmin.getTipStatus() != 0);
        loginUser.setUsername(authAdmin.getUsername());
        loginUser.setTenantId(0L);
        loginUser.setDisplayName(authAdmin.getUsername());
        authAdminService.updateOnlineStatus("1", authAdmin.getId());
        return SessionHelper.login(loginUser, loginModel);
    }

    private static void validatePassWord(LoginReq loginReq, AuthAdmin authAdmin) {
        if (authAdmin == null) {
            throw new JsonException(ResultEnum.DATA_NOT, "用户名或密码错误");
        }

        if (!PasswordUtils.authAdminPwd(loginReq.getPassword()).equals(authAdmin.getPassword())) {
            throw new JsonException(ResultEnum.DATA_NOT, "用户名或密码错误");
        }
    }

    @Override
    public void logOut(HttpServletRequest request, HttpServletResponse response) {
        SessionHelper.logout(request, response);
        // 刷新权限缓存
        this.refreshPermissionCache(SessionContextHolder.getId());
    }

    @Override
    public List<String> getPermissionFromCache(String userId) {

        List<String> authRules = new ArrayList<>();
        String redisKey = LOGIN_PERMISSION_KEY_PREFIX + userId;
        if (CacheUtils.hasKey(redisKey)) {
            return new ArrayList<>(CacheUtils.sGetMembers(redisKey));
        }
        log.info("开始获取数据库中的用户的权限规则列表");

        // 获取角色ids
        List<AuthRoleAdmin> authRoleAdmins = authRoleAdminService.listByAdminId(userId);

        List<Long> roleIds = authRoleAdmins.stream().map(AuthRoleAdmin::getRoleId).collect(Collectors.toList());

        // 角色授权列表
        List<AuthPermission> authPermissions = authPermissionService.listByRoleIdIn(roleIds);
        List<Long> permissionRuleIds  = authPermissions.stream().map(AuthPermission::getPermissionRuleId).collect(Collectors.toList());

        // 获取授权的规则
        List<AuthPermissionRule> authPermissionRules = authPermissionRuleService.listByIdIn(permissionRuleIds);

        // 获取权限列表
        authRules = authPermissionRules.stream().map(AuthPermissionRule::getName).collect(Collectors.toList());



        String[] strings = authRules.toArray(new String[0]);
        if (strings.length == 0) return authRules;
        CacheUtils.sAdd(redisKey, strings);
        CacheUtils.expire(redisKey, 3600L); // 1小时后过期
        return authRules;
    }

    @Override
    public void refreshPermissionCache(String userId) {
        Objects.requireNonNull(userId, "缺少 userId");
        CacheUtils.delete(LOGIN_PERMISSION_KEY_PREFIX + userId);
    }

}
